Skip to content

fix(local-ci): per-leg TMPDIR so concurrent legs cannot fail each other's temp-root hygiene scans - #112

Merged
ScriptedAlchemy merged 2 commits into
mainfrom
cursor/local-ci-per-leg-tmpdir-8487
Sep 1, 2026
Merged

fix(local-ci): per-leg TMPDIR so concurrent legs cannot fail each other's temp-root hygiene scans#112
ScriptedAlchemy merged 2 commits into
mainfrom
cursor/local-ci-per-leg-tmpdir-8487

Conversation

@ScriptedAlchemy

@ScriptedAlchemy ScriptedAlchemy commented Sep 1, 2026

Copy link
Copy Markdown
Owner

Fixes #110

Problem

pnpm check:local-ci runs four legs concurrently (verify on Node 22.19/24/26 plus the gates leg) on one machine, and all of them shared the system /tmp. packages/agent-bundle/tests/cli.test.ts asserts temp-root hygiene: it snapshots agent-bundle-artifact-* directories under os.tmpdir() and fails if new ones appear during its scan window. A sibling leg's legitimate in-flight artifact-inspection directory could land inside another leg's window and fail the assertion — cross-leg contamination, not a product defect (observed while landing #86).

Fix

Each leg now gets a private TMPDIR under the run's scratch root — .worktrees/local-ci/tmp/<leg>, recreated at the start of every run — applied through the same buildLegEnvironment overrides that carry the leg's worker caps. Every leg's temp traffic, and the test's os.tmpdir() scan (Node resolves it from TMPDIR), is naturally scoped to the leg; this also isolates any other shared-/tmp assumptions. cli.test.ts is untouched and keeps its strictness: a directory leaked by the leg's own process tree still lands in the leg's temp root and still fails its own scan. Recreating the directory each run also drops anything a killed prior run left behind.

docs/local-ci.md (the workflow contract the script header says to keep in sync) documents the per-leg temp root. No changeset: this is repo tooling, not a user-visible package change (.changeset/README.md scopes changesets to package changes; prior scripts/tests-only PRs such as #109 carry none).

Acceptance evidence

Simulated the two-concurrent-legs topology directly against the real integration pool on Node 22.19.0 (a full four-leg check:local-ci needs Node 22.19/24/26 installs the verification machine doesn't carry):

  • Cross-leg immunity: two concurrent pnpm test:integration:run packages/agent-bundle/tests/cli.test.ts runs, each with its own scoped TMPDIR exactly as the runner now sets it, while a contaminator process created a fresh agent-bundle-artifact-* directory in the shared /tmp every 50 ms (318 directories over the run — guaranteed to land inside any /tmp scan window, i.e. the old failure mode). Both runs green, exit 0.
  • Leak detection intact: the same run with the contaminator writing into the leg's own TMPDIR — the suite fails on exactly the hygiene assertion (expect(after).toEqual(before), cli.test.ts:269) in runs MCP and hook operations from a packed consumer with explicit and temporary artifacts, exit 1. A directory created inside the leg's temp root (what a real leak from the leg's process tree now looks like) is still caught.

Also green after the change: a lone cli.test.ts run under a scoped TMPDIR, and pnpm lint (0 errors, 0 warnings).

Open in Web Open in Cursor 

…ach other's temp-root hygiene scans

Four concurrent legs shared the system /tmp, so cli.test.ts's hygiene
assertion (snapshot agent-bundle-artifact-* under os.tmpdir(), fail on new
ones) could see a sibling leg's legitimate in-flight artifact-inspection
directory and fail. Each leg now gets a private TMPDIR under the run's
scratch root (.worktrees/local-ci/tmp/<leg>), recreated every run, so every
leg's temp traffic — and the test's scan — is naturally scoped. The
assertion keeps its strictness: a directory leaked by the leg's own process
tree still lands in the leg's temp root and still fails its scan.

Co-authored-by: Zack Jackson <ScriptedAlchemy@users.noreply.github.com>
@changeset-bot

changeset-bot Bot commented Sep 1, 2026

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: b1261a3

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@ScriptedAlchemy
ScriptedAlchemy marked this pull request as ready for review September 1, 2026 00:31
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 1, 2026

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review Completed 2026-09-01T00:32:55.600450Z 219e2c5 Draft marked ready
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@ScriptedAlchemy

Copy link
Copy Markdown
Owner Author

Landing attempt blocked: repo-nested per-leg TMPDIR breaks Chrome's AF_UNIX socket path limit

The gate run at 219e2c5 failed deterministically on all three verify legs (Node 22.19 / 24 / 26). Every browser integration test crashed at Chrome launch because the per-leg TMPDIR was placed under the repo worktree (.worktrees/local-ci/tmp/<leg>), and Chrome creates AF_UNIX sockets inside TMPDIR. The kernel limits sun_path to 108 bytes; the nested path measured 126 chars (even from the primary checkout it would be ~106 — no headroom).

Key evidence (from the node22 leg; node24/node26 are identical):

[1900540:1900540:0901/003512.376558:FATAL:chrome/browser/process_singleton_posix.cc:313] Socket path too long: /fast/projects/agent-bundle/.worktrees/land-112/.worktrees/local-ci/tmp/verify-node22/com.google.Chrome.ZX3yG0/SingletonSocket.
[pid=1900540] <process did exit: exitCode=null, signal=SIGABRT>
browserType.launch: Target page, context or browser has been closed

and the same fatal on node24/node26:

2033186:0901/003635.928475:FATAL:chrome/browser/process_singleton_posix.cc:313] Socket path too long: .../.worktrees/local-ci/tmp/verify-node24/...
2027489:0901/003632.007376:FATAL:chrome/browser/process_singleton_posix.cc:313] Socket path too long: .../.worktrees/local-ci/tmp/verify-node26/...

Fix being pushed: keep the per-leg private TMPDIR (the #110 isolation is correct), but relocate it to a short, stable path under the system temp root: join(os.tmpdir(), 'abci-<hash8>-<leg>'), where <hash8> is the first 8 hex chars of SHA-256 of the repo root path — stable per repo (reruns reuse it; rm -rf + mkdir -p at run start prevents accumulation) and collision-free across concurrent runs from different repos. Resulting paths are ~25–35 chars, leaving comfortable headroom under 108 bytes. Isolation semantics are unchanged: each leg's os.tmpdir() still resolves to its own directory, so the cli.test.ts hygiene scan stays scoped per leg.

…yte limit)

Chrome creates AF_UNIX sockets inside TMPDIR; the repo-nested
.worktrees/local-ci/tmp/<leg> path overflowed the kernel's 108-byte
sun_path limit (126 chars measured) and crashed every browser
integration test at launch on all three verify legs. Keep the per-leg
private TMPDIR (#110 isolation unchanged) but place it at
os.tmpdir()/abci-<hash8>-<leg>, where <hash8> is SHA-256 of the repo
root — stable per repo so reruns reuse (and rm -rf + mkdir reset) it,
and concurrent runs from different checkouts cannot collide. Longest
leg path is 32 chars, leaving ~35 bytes of socket-name headroom.
@ScriptedAlchemy

Copy link
Copy Markdown
Owner Author

Gate evidence: GREEN at b1261a3

Full pnpm check:local-ci in a fresh scratch worktree at the pushed head — 1 attempt, no retries, no flakes observed:

Local CI gate at b1261a35 — 4 leg(s), 96 cores
verify-node22  test:integration  pass  2m21s  556/558 passed, 2 skipped, 51 files
verify-node24  test:integration  pass  2m16s  556/558 passed, 2 skipped, 51 files
verify-node26  test:integration  pass  2m13s  556/558 passed, 2 skipped, 51 files
gates-node22   examples:check pass 4m48s · check:release pass 5m22s · eval:spot pass 13s
Wall time 10m33s
Local CI gate: GREEN

Merging.

@ScriptedAlchemy
ScriptedAlchemy merged commit 8bf9618 into main Sep 1, 2026
8 of 9 checks passed
@ScriptedAlchemy
ScriptedAlchemy deleted the cursor/local-ci-per-leg-tmpdir-8487 branch September 1, 2026 01:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

local-ci: concurrent legs share /tmp and can fail cli.test.ts artifact hygiene assertion

2 participants